/*
 * This file is part of Advanced ABX
 *
 * Copyright 2010 Christian Uebber
 *
 * Advanced ABX is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation.
 *
 * Advanced ABX is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Advanced ABX.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.uebber.sound.abx.controller;

import de.uebber.sound.abx.model.ABXBean;
import de.uebber.sound.abx.model.ABXDecision;

import java.util.Locale;

/**
 * Computes ABX results and outputs them as formatted text.
 *
 * @author Christian Uebber
 */
public class ABXProcessor {

    private ABXBean bean;

    private final int totalRounds;
    private int rounds;
    private int correctAnswers = 0;

    public ABXProcessor(int totalRounds, ABXBean bean) {
        this.totalRounds = totalRounds;
        this.bean = bean;
    }

    public boolean processDecision(ABXDecision decision) {
        rounds++;
        if (decision.correct)
            correctAnswers++;
        bean.publish(String.format(Locale.US, "%1$tF %1$tT - Correct answers: %2$d/%3$d (p=%4$.5f)", decision.time,
                correctAnswers, totalRounds, getP(totalRounds, correctAnswers)));
        if (rounds >= totalRounds) {
            float p = getP(totalRounds, correctAnswers);
            bean.publish(String.format(Locale.US, "\nTest finished!\n\n" +
                    "Probability of guessing: %.1f%% (p=%.5f)", p * 100, p));
            return false;
        } else {
            return true;
        }
    }

    public static float getP(int n, int k) {
        float ret = 0;
        for (int i = k; i <= n; i++) {
            ret += binomialProbability(0.5d, n, i);
        }
        return ret;
    }

    /**
     * Computes binomial probability.
     *
     * @param p Probability of success in the range 0-1
     * @param n Number of trials
     * @param k Number of successes
     * @return Binomial probability in percent.
     */
    public static double binomialProbability(double p, int n, int k) {
        return choose(n, k) * Math.pow(p, k) * Math.pow(1d - p, n - k);
    }

    public static long choose(int n, int k) {
        if (k > n)
            return 0;

        double accum = 1;
        for (int i = 1; i <= k; i++) {
            accum *= (double) (n - k + i) / i;
        }
        return Math.round(accum);
    }

    public static long faculty(int n) {
        long ret = 1;
        for (int i = 2; i <= n; i++) {
            ret *= i;
        }
        return ret;
    }
}
